import React, { HTMLAttributes, ReactElement, useEffect, useState } from 'react';
import styled from 'styled-components';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import './index.scss';
import RightIcon from '../icons/right.svg';
export interface CarouselProps extends HTMLAttributes {
dot?: boolean;
autoPlay?: boolean;
callback?: (index: number) => void;
children?: React.ReactNode;
}
// 不知道react的虚拟节点什么类型所以扩充vNode类型来消除ts警告
type VNode = ReactElement & { type: { name: string } };
const CarouselStyled = styled.div`
position: relative;
overflow: hidden;
width: 100%;
height: 100%;
`;
const ActionWrap = styled.div`
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
`;
type SpanWrapProps = {
dot: boolean;
};
const SpanWrap = styled.div`
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: ${(props: SpanWrapProps) => (props.dot ? 'flex' : 'none')};
gap: 10px;
`;
type SpanProp = {
selfOrder: number;
currentOrder: number;
};
const SpanRadius = styled.span`
width: 10px;
height: 10px;
border-radius: 50%;
cursor: pointer;
background-color: ${(props: SpanProp) =>
props.selfOrder === props.currentOrder ? 'orange' : '#fff'};
`;
const CommonDot = styled.div`
position: absolute;
top: 50%;
transform: translateY(-50%);
background-color: transparent;
cursor: pointer;
&:hover {
background-color: #fff;
}
width: 2em;
height: 2em;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
`;
const LeftDot = styled(CommonDot)`
left: 10px;
`;
const RightDot = styled(CommonDot)`
right: 10px;
`;
const Carousel: React.FC = (props) => {
const { children, autoPlay, callback, dot, ...rest } = props;
const [index, setIndex] = useState(1);
const kids = children as ReactElement[];
useEffect(() => {
const len = kids.length;
let time: number;
if (autoPlay) {
time = window.setInterval(() => {
setIndex((state) => {
if (state >= len) {
state = 1;
} else {
state += 1;
}
callback!(state);
return state;
});
}, 4000);
}
return () => {
window.clearInterval(time);
};
}, []);
const render = () => {
return React.Children.map(children, (child) => {
const vNode = child as VNode;
if (
React.isValidElement(vNode) &&
vNode.props.name === 'CarouselItem' &&
vNode.props.order === index
) {
return vNode;
}
});
};
const createSpan = () => {
const dom: ReactElement[] = [];
for (let i = 0; i < kids.length; i++) {
dom.push();
}
return dom;
};
const selectedIndex = (e: React.MouseEvent) => {
const el = e.target as HTMLSpanElement;
if (el.tagName.toLowerCase() === 'span') {
const newOrder = parseInt(el.getAttribute('data-order') || '1', 10);
setIndex(newOrder);
callback!(newOrder);
}
};
const clickLeftIcon = () => {
if (index > 1) {
setIndex((state) => {
callback!(state - 1);
return state - 1;
});
} else {
callback!(kids.length);
setIndex(kids.length);
}
};
const clickRightIcon = () => {
if (index <= kids.length - 1) {
setIndex((state) => {
callback!(state + 1);
return state + 1;
});
} else {
callback!(1);
setIndex(1);
}
};
return (
{render()}
) => selectedIndex(e)} dot={dot!}>
{createSpan()}
);
};
Carousel.defaultProps = {
dot: true,
autoPlay: true,
callback: () => {},
children: []
};
export default Carousel;